//
/******************************************************************************
 Copyright (c) 2007 David Johnson <david@usermode.org>
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

 2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 //这个类主要参考上面这位程序猿，只能看懂大概的意思。

  Copyright (c) 2015.07   zack <153426331@qq.com>//参考如上。

*****************************************************************************/


#include "report.h"
#include "report_p.h"

#include <QPrinter>
#include <QPainter>
#include <QDebug>
#include <QFile>
#include <QApplication>
#include <QFileInfo>
#include <QDir>
#include <QPrintPreviewDialog>
#include <QTextDocument>
#include <QAbstractTextDocumentLayout>
#include <QTextFrame>

namespace YReport {



Report::Report(QObject *parent) :
    QObject(parent),
    p(new ReportPrivate())

{
    if (parent) p->parent_ = qobject_cast<QWidget*>(parent);
    p->printer_->setFullPage(true);
    p->printer_->setOrientation(QPrinter::Portrait);
}

Report::~Report()
{
    delete p;
}


void Report::setOutPutFilePath(const QString &path)
{
    p->printer_->setOutputFileName(QDir::fromNativeSeparators(path));
}



/*!
 * \brief REPORT::printerPixmap
 * \param pixmapPath
 *
 * 将图片保存为pdf
 *  QPrinter printer(QPrinter::HighResolution);
    printer.setPageSize(QPrinter::A4);  //设置纸张大小为A4
    printer.setOutputFormat(QPrinter::PdfFormat);  //设置输出格式为pdf
    printer.setOutputFileName(QDir::fromNativeSeparators(path));   //设置输出路径

    QPixmap pixmap(QDir::fromNativeSeparators(pixmapPath));

    QPainter painter;
    painter.begin(&printer);
    QRect rect = painter.viewport();
    int multiple = rect.width()/pixmap.width();
    painter.scale(multiple, multiple); //将图像(所有要画的东西)在pdf上放大multiple-1倍
    painter.drawPixmap(0, 0, pixmap);  //画图
    painter.end();
 *  将一张图片保存为pdf
 * //参照自：http://blog.sina.com.cn/s/blog_a6fb6cc90101gvnx.html
 */
//void Report::pixmapToPdf(const QString &pixmapPath, const QString &path)
//{
//    QPrinter pr(QPrinter::HighResolution);
//    pr.setPageSize(QPrinter::A4);  //设置纸张大小为A4
//    pr.setOutputFormat(QPrinter::PdfFormat);  //设置输出格式为pdf
//    pr.setOutputFileName(QDir::fromNativeSeparators(path));   //设置输出路径

//    QPixmap pixmap(QDir::fromNativeSeparators(pixmapPath));

//    QPainter pa;
//    pa.begin(&pr);
//    QRect rect = pa.viewport();
//    int multiple = rect.width()/pixmap.width();
//    pa.scale(multiple, multiple); //将图像(所有要画的东西)在pdf上放大multiple-1倍
//    pa.drawPixmap(0, 0, pixmap);  //画图
//    pa.end();

//}


/*!
   \brief Report::pageSize
   \return  Return the page size. This is defined by the
   QPrinter::PageSize  enumeration.
 */
QPrinter::PageSize Report::pageSize() const
{
    return p->printer_->pageSize();
}

/*!
   \brief Report::setPageSize
   \param size
   Set the page size. Size is defined by the QPrinter::PageSize enumeration.
   By default, this is QPrinter::Letter in US and Canada locales, and
   QPrinter::A4 in other locales.
 */
void Report::setPageSize(QPrinter::PageSize size)
{
    p->printer_->setPageSize(size);
}

/*!
   \brief Report::orientation
   \return
   Return the page orientation. This is defined by the QPrinterOrientation
   enumeration.
 */
QPrinter::Orientation Report::orientation() const
{
    return p->printer_->orientation();
}

/*!
   \brief Report::setOrientation
   \param orientation
    Set the page orientation. Orientation is defined by the
    QPrinter::Orientation enumeration, and can be QPrinter::Portrait (the
    default) or QPrinter::Landscape.
 */
void Report::setOrientation(QPrinter::Orientation orientation)
{
    p->printer_->setOrientation(orientation);
}


//void Report::print(QPrinter *printer)
//{
//    if (!printer || !p->document_) return;

//    p->document_->setUseDesignMetrics(true);
//    p->document_->documentLayout()->setPaintDevice(printer);


//    QRectF rect = p->printer_->paperRect();

//    rect.adjust(mmToInches(15) * printer->logicalDpiX(),
//                mmToInches(15) * printer->logicalDpiY(),
//                -mmToInches(15) * printer->logicalDpiX(),
//                -mmToInches(15) * printer->logicalDpiY());
//    p->document_->setPageSize(rect.size());
//    // dump existing margin (if any)
//    QTextFrameFormat fmt = p->document_->rootFrame()->frameFormat();
//    fmt.setMargin(0);
//    p->document_->rootFrame()->setFrameFormat(fmt);

//    // to iterate through pages we have to worry about
//    // copies, collation, page range, and print order

//    // get num copies
//    int doccopies;
//    int pagecopies;
//    if (printer->collateCopies()) {
//        doccopies = 1;
//        pagecopies = printer->numCopies();
//    } else {
//        doccopies = printer->numCopies();
//        pagecopies = 1;
//    }

//    // get page range
//    int firstpage = printer->fromPage();
//    int lastpage = printer->toPage();
//    if (firstpage == 0 && lastpage == 0) { // all pages
//        firstpage = 1;
//        lastpage = p->document_->pageCount();
//    }

//    // print order
//    bool ascending = true;
//    if (printer->pageOrder() == QPrinter::LastPageFirst) {
//        int tmp = firstpage;
//        firstpage = lastpage;
//        lastpage = tmp;
//        ascending = false;
//    }

//    // loop through and print pages
//    QPainter painter(printer);
//    painter.setRenderHints(QPainter::Antialiasing |
//                           QPainter::TextAntialiasing |
//                           QPainter::SmoothPixmapTransform, true);
//    for (int dc=0; dc<doccopies; dc++) {
//        int pagenum = firstpage;
//        while (true) {
//            for (int pc=0; pc<pagecopies; pc++) {
//                if (printer->printerState() == QPrinter::Aborted ||
//                    printer->printerState() == QPrinter::Error) {
//                    return;
//                }
//                // print page
//                paintPage(&painter, p->document_, pagenum);
//                if (pc < pagecopies-1) printer->newPage();
//            }
//            if (pagenum == lastpage) break;
//            if (ascending) pagenum++;
//            else           pagenum--;
//            printer->newPage();
//        }

//        if (dc < doccopies-1) printer->newPage();
//    }
//}


//void Report::paintPage(QPainter *painter, QTextDocument *document, int pagenum)
//{
////    QRectF rect;
//    double onepoint = painter->device()->logicalDpiY() / 72.0;

//    // header
//    if (headersize_ > 0) {
//        rect = headerRect(painter->device());
//        if (headerrule_ > 0.0) {
//            painter->save();
//            // allow space between rule and header
//            painter->translate(0, onepoint + (headerrule_ * onepoint / 2.0));
//            painter->setPen(QPen(Qt::black, headerrule_ * onepoint));
//            painter->drawLine(rect.bottomLeft(), rect.bottomRight());
//            painter->restore();
//        }

//        // replace page variables
//        QString header = headertext_;
//        header.replace("&page;", QString::number(pagenum));
//        if (dateformat_.isEmpty()) {
//            header.replace("&date;", QDate::currentDate().toString());
//        } else {
//            header.replace("&date;", QDate::currentDate().toString(dateformat_));
//        }


//        painter->save();
//        painter->translate(rect.left(), rect.top());
//        QRectF clip(0, 0, rect.width(), rect.height());
//        QTextDocument doc;
//        doc.setUseDesignMetrics(true);
//        doc.setHtml(header);
//        doc.documentLayout()->setPaintDevice(painter->device());
//        doc.setPageSize(rect.size());

//        // align text to bottom
//        double newtop = clip.bottom() - doc.size().height();
//        clip.setHeight(doc.size().height());
//        painter->translate(0, newtop);

//        doc.drawContents(painter, clip);
//        painter->restore();
//    }

//    // footer
//    if (footersize_ > 0) {
//        rect = footerRect(painter->device());
//        if (footerrule_ > 0.0) {
//            painter->save();
//            // allow space between rule and footer
//            painter->translate(0, -onepoint + (-footerrule_ * onepoint / 2.0));
//            painter->setPen(QPen(Qt::black, footerrule_ * onepoint));
//            painter->drawLine(rect.topLeft(), rect.topRight());
//            painter->restore();
//        }

//        // replace page variables
//        QString footer = footertext_;
//        footer.replace("&page;", QString::number(pagenum));
//        if (dateformat_.isEmpty()) {
//            footer.replace("&date;", QDate::currentDate().toString());
//        } else {
//            footer.replace("&date;", QDate::currentDate().toString(dateformat_));
//        }

//        painter->save();
//        painter->translate(rect.left(), rect.top());
//        QRectF clip(0, 0, rect.width(), rect.height());
//        QTextDocument doc;
//        doc.setUseDesignMetrics(true);
//        doc.setHtml(footer);
//        doc.documentLayout()->setPaintDevice(painter->device());
//        doc.setPageSize(rect.size());
//        doc.drawContents(painter, clip);
//        painter->restore();
//    }

    // content
//    painter->save();

////    rect = contentRect(painter->device());

//            QPaintDevice *device = painter->device();
//    QRectF rect = p->printer_->paperRect();
//        // adjust for DPI
//        rect.setWidth(rect.width() *
//                      device->logicalDpiX() / p->printer_->logicalDpiX());
//        rect.setHeight(rect.height() *
//                      device->logicalDpiY() / p->printer_->logicalDpiY());

//    rect.adjust(millimeterToInches(15) * p->printer_->logicalDpiX(),
//                millimeterToInches(15) * p->printer_->logicalDpiY(),
//                -millimeterToInches(15) * p->printer_->logicalDpiX(),
//                -millimeterToInches(15) * p->printer_->logicalDpiY());
//    painter->translate(rect.left(), rect.top() - (pagenum-1) * rect.height());
//    QRectF clip(0, (pagenum-1) * rect.height(), rect.width(), rect.height());

//    document->drawContents(painter, clip);

//    painter->restore();

//}

/*!
   \brief Report::footerText
   \return
   Return the text for the footer.
 */
QString Report::footerText() const
{
    return p->footerText_;
}

/*!
   \brief Report::setFooterText
   \param footerText
    Set the text for the footer. Rich text is supported. HTML tags may be used
    to format the text and align elements. The following page variables may be
    included in the text:
    - \&page; - Insert current page number
    - \&date; - Insert current date, using the format set with setPageFormat()
 */
void Report::setFooterText(const QString &footerText)
{
    p->footerText_ = footerText;
}

/*!
   \brief Report::footerRule
   \return
   Return the size of the footer rule in points. One point is 1/72 inch.
 */
qreal Report::footerRule() const
{
    return p->footerRule_;
}

/*!
   \brief Report::setFooterRule
   \param footerRule
    Set the footer rule size in points. By default, the footer rule is one half
    point (1/144 inch). To turn off the rule, set the rule size to 0. The rule
    will be  drawn just above the footer area.
 */
void Report::setFooterRule(const qreal &footerRule)
{
    p->footerRule_ = qMax(0.0, footerRule);
}

/*!
   \brief Report::footerSize
   \return
   Return the height of the footer block in millimeters.
 */
qreal Report::footerSize() const
{
    return p->footerSize_;
}

/*!
   \brief Report::setFooterSize
   \param footerSize
    Set the height of the footer in millimeters. The default is zero (no
    header).
 */
void Report::setFooterSize(const qreal &footerSize)
{
    if ((footerSize > 0) && (footerSize <= p->printer_->paperRect().height() / 8)) {
        p->footerSize_ = footerSize;
    } else {
        p->footerSize_ = 0;
    }
}

/*!
   \brief Report::headerText
   \return
   Return the text for the header.
 */
QString Report::headerText() const
{
    return p->headerText_;
}

/*!
   \brief Report::setHeaderText
   \param headerText
    Set the text for the header. Rich text is supported. HTML tags may be used
    to format the text and align elements. The following page variables may be
    included in the text:
    - \&page; - Insert current page number
    - \&date; - Insert current date, using the format set with setPageFormat()
 */
void Report::setHeaderText(const QString &headerText)
{
    p->headerText_ = headerText;
}

/*!
   \brief Report::headerRule
   \return
   Return the size of the header rule in points. One point is 1/72 inch.
 */
qreal Report::headerRule() const
{
    return p->headerRule_;
}

/**
    \brief Report::setHeaderRule
    \param headerRule
    Set the header rule size in points. By default, the header rule is one half
    point (1/144 inch). To turn off the rule, set the rule size to 0. The rule
    will be  drawn below the header area.
 */
void Report::setHeaderRule(const double &headerRule)
{
    p->headerRule_ = qMax(0.0, headerRule);
}

/*!
   \brief Report::headerSize
   \return
   Return the height of the header block  in millimeters.
 */
qreal Report::headerSize() const
{
    return p->headerSize_;
}

/*!
   \brief Report::setHeaderSize
   \param headerSize
    Set the height of the header in millimeters. The default is zero (no
    header).
 */
void Report::setHeaderSize(const qreal &headerSize)
{
    if ((headerSize > 0) && (headerSize <= p->printer_->paperRect().height() / 8)) {
        p->headerSize_ = headerSize;
    } else {
        p->headerSize_ = 0;
    }
}

/*!
   \brief Report::bottomMargin
   \return
   Return the bottom page margin size in millimeters.
 */
qreal Report::bottomMargin() const
{
    return p->bottomMargin_;
}

/*!
   \brief Report::setBottomMargin
   \param bottomMargin
   Set the bottom margin size in millimeters. The default value is 15mm.
 */
void Report::setBottomMargin(const qreal &bottomMargin)
{
    p->bottomMargin_ = qMax(0.0, bottomMargin);
}

/*!
   \brief Report::topMargin
   \return
   Return the top page margin size in millimeters.
 */
qreal Report::topMargin() const
{
    return p->topMargin_;
}

/*!
   \brief Report::setTopMargin
   \param topMargin
   Set the top margin size in millimeters. The default value is 15mm.
 */
void Report::setTopMargin(const qreal &topMargin)
{
    p->topMargin_ = qMax(0.0, topMargin);
}

/*!
   \brief Report::leftMargin
   \return
   Return the left page margin width in millimeters.
 */
qreal Report::leftMargin() const
{
    return p->leftMargin_;
}

/*!
   \brief Report::setLeftMargin
   \param leftMargin
   Set the left margin width in millimeters. The default value is 15mm.
 */
void Report::setLeftMargin(const qreal &leftMargin)
{
    p->leftMargin_ = qMax(0.0, leftMargin);
}


/*!
   \brief Report::rightMargin
   \return
   Return the right page margin width in millimeters.
 */
qreal Report::rightMargin() const
{
    return p->rightMargin_;
}

/*!
   \brief Report::setRightMargin
   \param rightMargin
   Set the right margin width in millimeters. The default value is 15mm.
 */
void Report::setRightMargin(const qreal &rightMargin)
{
    p->rightMargin_ = qMax(0.0, rightMargin);
}


/*!
   \brief Report::spacing
   \return
    Return the spacing between the page content and the header and footer
    blocks. This is defined in millimeters.
 */
qreal Report::spacing() const
{
    return p->spacing_;
}

/*!
   \brief Report::setSpacing
   \param spacing
   Set the spacing between the page content and the header and footer blocks.
   The default value is 5mm.
 */
void Report::setSpacing(const qreal &spacing)
{
    if ((spacing > 0) && (spacing <= p->printer_->paperRect().height() / 8)) {
        p->spacing_ = spacing;
    } else {
        p->spacing_ = 0;
    }
}

/*!
   \brief Report::setMargins
   \param margin
   Set all margins to have the identical size. The default value is 15mm.
 */
void Report::setMargins(qreal margin)
{
    if ((margin > 0)
        && (margin < p->printer_->paperRect().height() / 2)
        && (margin < p->printer_->paperRect().width() / 2)) {
        p->leftMargin_ = p->rightMargin_ = p->topMargin_ = p->bottomMargin_ = margin;
    } else {
        p->leftMargin_ = p->rightMargin_ = p->topMargin_ = p->bottomMargin_ = 0;
    }
}

QPrinter *Report::printer() const
{
    return p->printer_;
}

QWidget *Report::parent() const
{
    return p->parent_;
}

//void Report::setPrinter(QPrinter *printer)
//{
//    printer_ = printer;
//}

/*!
   \brief Report::paperRect
   \param device
   \return
   Return the size of the paper, adjusted for DPI
 */
QRectF Report::paperRect(QPaintDevice *device)
{
    // calculate size of paper
    QRectF rect = p->printer_->paperRect();
    // adjust for DPI
    rect.setWidth(rect.width() *
                  device->logicalDpiX() / p->printer_->logicalDpiX());
    rect.setHeight(rect.height() *
                  device->logicalDpiY() / p->printer_->logicalDpiY());

    return rect;
}

/*!
   \brief Report::contentRect
   \param device
   \return
   calculate the rect for the content block
 */
QRectF Report::contentRect(QPaintDevice *device)
{
    // calculate size of content (paper - margins)
    QRectF rect = paperRect(device);

    rect.adjust(millimeterToInches(p->leftMargin_) * device->logicalDpiX(),
                millimeterToInches(p->topMargin_) * device->logicalDpiY(),
                -millimeterToInches(p->rightMargin_) * device->logicalDpiX(),
                -millimeterToInches(p->bottomMargin_) * device->logicalDpiY());

    // header
    if (p->headerSize_ > 0) {
        rect.adjust(0, millimeterToInches(p->headerSize_) * device->logicalDpiY(), 0, 0);
        rect.adjust(0, millimeterToInches(p->spacing_) * device->logicalDpiY(), 0, 0);
    }
    // footer
    if (p->footerSize_ > 0) {
        rect.adjust(0, 0, 0, -millimeterToInches(p->footerSize_) * device->logicalDpiY());
        rect.adjust(0, 0, 0, -millimeterToInches(p->spacing_) * device->logicalDpiY());
    }

    return rect;
}

/*!
   \brief Report::headerRect
   \param device
   \return
   calculate the rect for the header block
 */
QRectF Report::headerRect(QPaintDevice *device)
{
    QRectF rect = paperRect(device);
    rect.adjust(millimeterToInches(p->leftMargin_) * device->logicalDpiX(),
                millimeterToInches(p->topMargin_) * device->logicalDpiY(),
                -millimeterToInches(p->rightMargin_) * device->logicalDpiX(), 0);

    (p->headerRule_ / 144.0);

    rect.setBottom(rect.top() +
                   millimeterToInches(p->headerSize_) * device->logicalDpiY());

    return rect;
}

/*!
   \brief Report::footerRect
   \param device
   \return
   calculate the rect for the footer block
 */
QRectF Report::footerRect(QPaintDevice *device)
{
    QRectF rect = paperRect(device);
    rect.adjust(millimeterToInches(p->leftMargin_) * device->logicalDpiX(), 0,
                -millimeterToInches(p->rightMargin_) * device->logicalDpiX(),
                -millimeterToInches(p->bottomMargin_) * device->logicalDpiY());

    rect.setTop(rect.bottom() -
                millimeterToInches(p->footerSize_) * device->logicalDpiY());

    return rect;
}

} //namespace YReport


ReportPrivate::ReportPrivate() :
    printer_(new QPrinter(QPrinter::HighResolution)),
    leftMargin_(15.0), rightMargin_(15.0), topMargin_(15.0),
    bottomMargin_(15.0), spacing_(5.0),
    headerSize_(0.0), headerRule_(0.5), headerText_(QString()),
    footerSize_(0.0), footerRule_(0.5), footerText_(QString())
{
}

ReportPrivate::~ReportPrivate()
{
    delete printer_;
}






